package com.lxk.util.netty;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lxk.util.netty.CRC8;
import com.lxk.vo.CurrentRedisVO;
import com.lxk.vo.NettyDataVO;
import com.lxk.vo.ScanDataVO;
import io.netty.buffer.ByteBuf;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * @author JohnSoybean
 * @create 2020/7/13 11:27
 * @description 硬件接口协议处理方法类
 */
@Slf4j
public class NettyDataHandler {

    /**
     * 帧头
     */
    private final static byte[] head = new byte[]{(byte) 0xA5, (byte) 0xA5, (byte) 0x5A, (byte) 0x5A};

    /**
     * 设备类型：排队扫码机
     */
    private final static byte[] deviceTypeQueue = new byte[]{(byte) 0x01, (byte) 0x01};

    /**
     * 设备类型：扫码确认机
     */
    private final static byte[] deviceTypeConfirm = new byte[]{(byte) 0x01, (byte) 0x02};

    /**
     * 主动上报：MCU->服务器
     */
    private final static byte[] orderTypeUp = new byte[]{(byte) 0xF8, (byte) 0x0B};

    /**
     * 主动上报返回：服务器->MCU
     */
    private final static byte[] orderTypeDown = new byte[]{(byte) 0xFA, (byte) 0x0B};

    /**
     * 上报类型：生成连接、扫码上报、心跳上报、结束游戏上报
     */
    private final static byte reportConnect = (byte) 0x01;
    private final static byte reportScan = (byte) 0x02;
    private final static byte reportHeartbeat = (byte) 0x03;
    private final static byte reportEnd = (byte) 0x04;

    /**
     * 心跳数据
     */
    private final static byte[] heartbeatData = new byte[]{(byte) 0xAA};


    /**
     * 提取出ByteBuf中所有数据，并进行基本校验
     * @param in
     * @return
     */
    public static NettyDataVO parseResult(ByteBuf in) {
        byte[] bytes = new byte[in.readableBytes()];
        in.readBytes(bytes);
        NettyDataVO vo = new NettyDataVO();
        vo.setHead(Arrays.copyOfRange(bytes, 0, 4));
        vo.setCrc8(Arrays.copyOfRange(bytes, 4, 5));
        vo.setDeviceId(Arrays.copyOfRange(bytes, 5, 13));
        vo.setDeviceType(Arrays.copyOfRange(bytes, 13, 15));
        vo.setOrderType(Arrays.copyOfRange(bytes, 15, 17));
        vo.setDataLength(Arrays.copyOfRange(bytes, 17, 19));
        vo.setDataType(Arrays.copyOfRange(bytes, 19, 20));
        vo.setData(Arrays.copyOfRange(bytes, 20, bytes.length));
        //打印原始数据
        StringBuilder result = new StringBuilder();
        for (byte aByte : bytes) {
            result.append(String.format("%02X", aByte)).append(" ");
        }
        System.out.println(result.toString());
        //帧头校验
        if (!Arrays.equals(head, vo.getHead())) {
            log.error("帧头校验错误");
            return null;
        }
        //crc8校验
        byte crc8 = CRC8.calcCrc8(bytes, 5, bytes.length - 5);
        log.info("CRC8: " + String.format("%02X", crc8));
        if (vo.getCrc8()[0] != crc8) {
            log.error("CRC8校验错误");
            return null;
        }
        //是否为上报命令类型
        if (!Arrays.equals(orderTypeUp, vo.getOrderType())) {
            log.error("无法处理的命令类型");
            return null;
        }
        return vo;
    }

    /**
     * 将字节数组转为ASCII字符
     * @param bytes
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String byteToString(byte[] bytes) throws UnsupportedEncodingException {
        return new String(bytes, StandardCharsets.ISO_8859_1);
    }

    /**
     * Description: 负数byte转正int <BR>
     */
    public static Integer byteToInteger(Byte b) {
        return 0xff & b;
    }

    /**
     * 判断是否为设备连接上报
     * @param vo
     * @return
     */
    public static boolean isConnectReport(NettyDataVO vo) {
        byte[] dataType = vo.getDataType();
        return dataType[0] == reportConnect;
    }

    /**
     * 判断是否为心跳上报
     * @param vo
     * @return
     */
    public static boolean isHeartbeatReport(NettyDataVO vo) {
        byte[] lengthBytes = vo.getDataLength();
        byte[] dataType = vo.getDataType();
        byte[] data = vo.getData();
        return lengthBytes[1] == (byte) 0x02
                && dataType[0] == reportHeartbeat
                && Arrays.equals(data, heartbeatData);
    }

    /**
     * 判断是否为结束上报
     * @param vo
     * @return
     */
    public static boolean isScanReport(NettyDataVO vo) {
        byte[] dataType = vo.getDataType();
        return dataType[0] == reportScan;
    }

    /**
     * 判断是否为结束上报
     * @param vo
     * @return
     */
    public static boolean isEndReport(NettyDataVO vo) {
        byte[] dataType = vo.getDataType();
        return dataType[0] == reportEnd;
    }

    /**
     * 生成返回字节数组
     * @param vo
     * @param data
     * @return
     */
    public static byte[] generateResponseBytes(NettyDataVO vo, byte[] data) {
        //设置数据长度
        byte[] dataLengthBytes = new byte[2];
        int dataLength = data.length + 1;
        if (dataLength <= 0) {
            return null;
        } else if (dataLength < 256) {
            dataLengthBytes[1] = (byte) dataLength;
        } else {
            dataLengthBytes[0] = (byte) (dataLength / 256);
            dataLengthBytes[1] = (byte) (dataLength % 256);
        }
        //生成待校验串
        byte[] crc8DataBytes = new byte[14 + dataLength];
        System.arraycopy(vo.getDeviceId(), 0, crc8DataBytes, 0, 8);
        System.arraycopy(vo.getDeviceType(), 0, crc8DataBytes, 8, 2);
        System.arraycopy(orderTypeDown, 0, crc8DataBytes, 10, 2);
        System.arraycopy(dataLengthBytes, 0, crc8DataBytes, 12, 2);
        System.arraycopy(vo.getDataType(), 0, crc8DataBytes, 14, 1);
        System.arraycopy(data, 0, crc8DataBytes, 15, data.length);
        //设置crc8校验
        byte crc8Byte = CRC8.calcCrc8(crc8DataBytes);
        //组合完整串
        byte[] res = new byte[19 + dataLength];
        System.arraycopy(head, 0, res, 0, 4);
        res[4] = crc8Byte;
        System.arraycopy(crc8DataBytes, 0, res, 5, crc8DataBytes.length);
        return res;
    }

    /**
     * 生成返回字节数组简略版 数据仅返回 0x01
     * @param vo
     * @return
     */
    public static byte[] generateSimpleResponseBytes(NettyDataVO vo) {
        //设置数据长度
        byte[] dataLengthBytes = new byte[]{0, 2};
        //生成待校验串
        byte[] crc8DataBytes = new byte[16];
        System.arraycopy(vo.getDeviceId(), 0, crc8DataBytes, 0, 8);
        System.arraycopy(vo.getDeviceType(), 0, crc8DataBytes, 8, 2);
        System.arraycopy(orderTypeDown, 0, crc8DataBytes, 10, 2);
        System.arraycopy(dataLengthBytes, 0, crc8DataBytes, 12, 2);
        System.arraycopy(vo.getDataType(), 0, crc8DataBytes, 14, 1);
        crc8DataBytes[15] = (byte) 0x01;
        //设置crc8校验
        byte crc8Byte = CRC8.calcCrc8(crc8DataBytes);
        //组合完整串
        byte[] res = new byte[21];
        System.arraycopy(head, 0, res, 0, 4);
        res[4] = crc8Byte;
        System.arraycopy(crc8DataBytes, 0, res, 5, crc8DataBytes.length);
        return res;
    }

    /**
     * 扫码上报 提取 Data中的数据
     * @param data
     * @return
     */
    public static ScanDataVO parseScanData(byte[] data) {
        ScanDataVO scanData = new ScanDataVO();
        scanData.setScanType(new byte[]{data[0]});
        scanData.setQrLength(new byte[]{data[1]});
        int qrDataLength = data.length - 2;
        byte[] qrData = new byte[qrDataLength];
        System.arraycopy(data, 2, qrData, 0, qrDataLength);
        scanData.setQrData(qrData);
        return scanData;
    }

    /**
     * 结束上报 提取 Data中的数据
     * @param data
     * @return
     */
    public static ScanDataVO parseEndData(byte[] data) {
        ScanDataVO scanData = new ScanDataVO();
        scanData.setQrLength(new byte[]{data[0]});
        int qrDataLength = data.length - 1;
        byte[] qrData = new byte[qrDataLength];
        System.arraycopy(data, 1, qrData, 0, qrDataLength);
        scanData.setQrData(qrData);
        return scanData;
    }

    public static void main(String[] args) {
        String msg = "31595585843599";
        byte[] bytes = msg.getBytes(StandardCharsets.ISO_8859_1);
        StringBuilder result = new StringBuilder();
        for (byte aByte : bytes) {
            result.append(String.format("%02X", aByte));
            result.append(" ");
        }
        System.out.println(result);
    }

}
